# 代码功能说明：
# 实现了一个完整的两层神经网络训练流程
# 使用MNIST手写数字数据集进行训练和测试
# 采用随机梯度下降法(SGD)进行参数更新
# 记录了训练过程中的损失和准确率变化
# 可视化展示了训练集和测试集的准确率曲线
# 关键点：
# 使用反向传播法高效计算梯度
# 每完成一个epoch(遍历完所有训练数据)评估一次模型
# 学习率控制参数更新步长
# 批量训练(batch training)提高训练效率
# 最终可视化帮助理解模型训练过程

# 指定文件编码为UTF-8
# coding: utf-8

# 导入系统模块和操作系统模块
import sys, os
# 添加父目录到系统路径，以便导入父目录中的模块
sys.path.append(os.pardir)

# 导入NumPy数值计算库
import numpy as np
# 导入matplotlib的pyplot绘图模块
import matplotlib.pyplot as plt
# 从dataset.mnist模块导入MNIST数据加载函数
from dataset.mnist import load_mnist
# 从two_layer_net模块导入TwoLayerNet类
from two_layer_net import TwoLayerNet

# 加载MNIST数据集
# normalize=True表示对图像进行归一化处理(0~1范围)
# one_hot_label=True表示标签采用one-hot编码
(x_train, t_train), (x_test, t_test) = load_mnist(normalize=True, one_hot_label=True)

# 初始化两层神经网络
# 输入层784个神经元(28x28像素)
# 隐藏层50个神经元
# 输出层10个神经元(对应0-9数字分类)
network = TwoLayerNet(input_size=784, hidden_size=50, output_size=10)

# 设置训练参数
iters_num = 10000  # 总迭代次数
train_size = x_train.shape[0]  # 训练数据大小(60000)
batch_size = 100  # 每个批次的样本数
learning_rate = 0.1  # 学习率

# 初始化列表用于记录训练过程中的指标
train_loss_list = []  # 训练损失
train_acc_list = []  # 训练准确率
test_acc_list = []  # 测试准确率

# 计算每个epoch包含的迭代次数
iter_per_epoch = max(train_size / batch_size, 1)

# 开始训练循环
for i in range(iters_num):
    # 随机选择批次数据
    batch_mask = np.random.choice(train_size, batch_size)
    x_batch = x_train[batch_mask]  # 批次输入数据
    t_batch = t_train[batch_mask]  # 批次标签数据

    # 计算梯度(使用反向传播法)
    grad = network.gradient(x_batch, t_batch)

    # 更新网络参数(W1,b1,W2,b2)
    for key in ('W1', 'b1', 'W2', 'b2'):
        network.params[key] -= learning_rate * grad[key]

    # 计算并记录当前损失
    loss = network.loss(x_batch, t_batch)
    train_loss_list.append(loss)

    # 每个epoch结束时评估模型
    if i % iter_per_epoch == 0:
        # 计算训练集和测试集准确率
        train_acc = network.accuracy(x_train, t_train)
        test_acc = network.accuracy(x_test, t_test)
        # 记录准确率
        train_acc_list.append(train_acc)
        test_acc_list.append(test_acc)
        # 打印当前准确率
        print("train acc, test acc | " + str(train_acc) + ", " + str(test_acc))

# 绘制训练结果
markers = {'train': 'o', 'test': 's'}  # 标记样式
x = np.arange(len(train_acc_list))  # x轴数据
plt.plot(x, train_acc_list, label='train acc')  # 训练准确率曲线
plt.plot(x, test_acc_list, label='test acc', linestyle='--')  # 测试准确率曲线(虚线)
plt.xlabel("epochs")  # x轴标签
plt.ylabel("accuracy")  # y轴标签
plt.ylim(0, 1.0)  # y轴范围限制
plt.legend(loc='lower right')  # 图例位置
plt.show()  # 显示图形
